﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;

namespace Dycox.Reporting
{
    internal class TableLayoutInfo
    {
        /// <summary>
        /// Standard cell size, in millimeters.
        /// </summary>
        public static readonly SizeF DefaultStandardCellSize = new SizeF(30, 6.5f);

        public TableLayoutInfo(Table table)
        {
            _table = table;

            UpdateWidthsAndHeights();
            UpdateCellLayout();
        }

        private Table _table;

        public Table Table
        {
            get { return _table; }
        }


        private float[] _columnWidths;

        public float[] ColumnWidths
        {
            get { return _columnWidths; }
        }

        private float[] _rowHeights;

        public float[] RowHeights
        {
            get { return _rowHeights; }
        }

        private PhysicalCell[,] _physicalCells;

        public PhysicalCell[,] PhysicalCells
        {
            get { return _physicalCells; }
        }

        public SizeF GetStandardCellSize()
        {
            SizeF ss = _table.StandardCellSize;

            if (ss.IsEmpty)
            {
                ss = DefaultStandardCellSize;

                if (_table.Report.PageSettings.Unit == ReportPageUnit.Centimeter)
                {
                    ss.Width /= 10;
                    ss.Height /= 10;
                }
            }

            return ss;
        }

        internal void UpdateWidthsAndHeights()
        {
            Table table = _table;
            TableRowCollection rows = table.Rows;
            TableColumnCollection cols = table.Columns;
            int rowCount = rows.Count, colCount = cols.Count;

            if (_rowHeights == null || _rowHeights.Length != rowCount)
                _rowHeights = new float[rowCount];

            if (_columnWidths == null || _columnWidths.Length != colCount)
                _columnWidths = new float[colCount];

            if (table.AutoSize)
                UpdateWidthsAndHeightsAuto();
            else
                UpdateWidthsAndHeightsFixed();

        }

        private void UpdateWidthsAndHeightsAuto()
        {
            int index = 0;
            SizeF ss = GetStandardCellSize();

            foreach (var col in _table.Columns)
            {
                var unit = col.Width;
                float width;

                if (!unit.IsEmpty)
                {
                    switch (unit.Type)
                    {
                        case ElementUnitType.Literal:
                            width = unit.Value;
                            break;
                        case ElementUnitType.Percentage:
                            width = ss.Width * unit.Value;
                            break;
                        default:
                            throw new InvalidOperationException();
                    }
                }
                else
                {
                    width = ss.Width;
                }

                _columnWidths[index++] = width;
            }

            index = 0;
            foreach (var row in _table.Rows)
            {
                var unit = row.Height;
                float height;

                if (!unit.IsEmpty)
                {
                    switch (unit.Type)
                    {
                        case ElementUnitType.Literal:
                            height = unit.Value;
                            break;
                        case ElementUnitType.Percentage:
                            height = ss.Height * unit.Value;
                            break;
                        default:
                            throw new InvalidOperationException();
                    }
                }
                else
                {
                    height = ss.Height;
                }

                _rowHeights[index++] = height;
            }
        }

        private void UpdateWidthsAndHeightsFixed()
        {
            Table table = _table;
            SizeF size = table.Size;

            int noUnitCount = 0;
            float rest = size.Height;
            int rowCount = _rowHeights.Length;
            int colCount = _columnWidths.Length;
            TableRowCollection rows = table.Rows;
            TableColumnCollection cols = table.Columns;

            for (int r = 0; r < rowCount; r++)
            {
                TableRow row = rows[r];
                ElementUnit height = row.Height;

                if (!height.IsEmpty)
                {
                    ElementUnit unit = height;
                    if (unit.Type == ElementUnitType.Literal)
                        _rowHeights[r] = unit.Value;
                    else
                        _rowHeights[r] = size.Height * unit.Value;
                    rest -= _rowHeights[r];
                }
                else
                {
                    noUnitCount++;
                    _rowHeights[r] = float.NaN;
                }
            }

            if (noUnitCount > 0)
            {
                float h = rest / (float)noUnitCount;
                for (int r = 0; r < _rowHeights.Length; r++)
                {
                    if (float.IsNaN(_rowHeights[r]))
                        _rowHeights[r] = h;
                }
            }

            noUnitCount = 0;
            rest = size.Width;
            for (int c = 0; c < colCount; c++)
            {
                TableColumn col = cols[c];
                ElementUnit width = col.Width;

                if (!width.IsEmpty)
                {
                    ElementUnit unit = width;
                    if (unit.Type == ElementUnitType.Literal)
                        _columnWidths[c] = unit.Value;
                    else
                        _columnWidths[c] = size.Width * unit.Value;
                    rest -= _columnWidths[c];
                }
                else
                {
                    noUnitCount++;
                    _columnWidths[c] = float.NaN;
                }
            }
            if (noUnitCount > 0)
            {
                float w = rest / (float)noUnitCount;
                for (int c = 0; c < _columnWidths.Length; c++)
                {
                    if (float.IsNaN(_columnWidths[c]))
                        _columnWidths[c] = w;
                }
            }
        }

        internal void UpdateCellLayout()
        {
            Table table = _table;
            TableRowCollection rows = table.Rows;
            TableColumnCollection cols = table.Columns;
            int rowCount = rows.Count, colCount = cols.Count;

            ResetPhysicalCells();

            for (int r = 0; r < rowCount; r++)
            {
                TableRow row = rows[r];
                TableCellCollection cells = row.Cells;
                int cellIndex = 0;

                for (int c = 0; c < colCount; c++)
                {
                    PhysicalCell phycell = GetPhysicalCell(r, c);

                    if (phycell.Cell == null)
                    {
                        if (cellIndex < cells.Count)
                        {
                            TableCell cell = cells[cellIndex++];
                            phycell.SetCell(cell, CellOccupation.Normal);

                            for (int rs = 0; rs < cell.RowSpan; rs++)
                            {
                                for (int cs = 0; cs < cell.ColSpan; cs++)
                                {
                                    if (rs + cs > 0)
                                    {
                                        phycell = GetPhysicalCell(r + rs, c + cs);
                                        if (phycell != null)
                                        {
                                            phycell.SetCell(cell, (CellOccupation)(Math.Sign(rs) * 2 + Math.Sign(cs)));
                                        }
                                    }
                                }
                            }

                        }
                        else
                            break;
                    }
                }
            }
        }

        private void ResetPhysicalCells()
        {
            int rowCount = _table.Rows.Count, colCount = _table.Columns.Count;
            int len0, len1;

            if (_physicalCells != null)
            {
                len0 = _physicalCells.GetLength(0);
                len1 = _physicalCells.GetLength(1);
            }
            else
                len0 = len1 = -1;

            if (_physicalCells == null || len0 != rowCount || len1 != colCount)
            {
                if (_physicalCells != null)
                {
                    for (int r = 0; r < len0; r++)
                    {
                        for (int c = 0; c < len1; c++)
                        {
                            _physicalCells[r, c].Clear();
                        }
                    }                    
                }
                _physicalCells = new PhysicalCell[rowCount, colCount];
            }

            for (int r = 0; r < rowCount; r++)
            {
                for (int c = 0; c < colCount; c++)
                {
                    PhysicalCell pc;

                    pc = _physicalCells[r, c];
                    if (pc == null)
                        pc = _physicalCells[r, c] = new PhysicalCell(r, c);
                    else
                        pc.Clear();
                }
            }
        }

        private PhysicalCell GetPhysicalCell(int r, int c)
        {
            if (r >= 0 && r < _physicalCells.GetLength(0) && c >= 0 && c < _physicalCells.GetLength(1))
                return _physicalCells[r, c];
            else
                return null;
        }

        //internal RectangleF GetCellBounds(TableCell cell, float lineWidth)
        //{
        //    int r = cell.Row.Cells.IndexOf(cell);
        //    int c, len1 = _physicalCells.GetLength(1);
        //    float x = 0, y;

        //    for (c = 0; c < len1; c++)
        //    {

        //        if (_physicalCells[r, c].Cell == cell)
        //            break;
        //    }

        //    if (c == len1)
        //        throw new InvalidOperationException("Cell not found.");



        //}
    }
}
